package net.tarine.gastronomi.server.services;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;

import net.tarine.gastronomi.client.services.ForumService;
import net.tarine.gastronomi.server.ToConverter;
import net.tarine.gastronomi.server.model.ForumMessage;
import net.tarine.gastronomi.server.model.ForumThread;
import net.tarine.gastronomi.server.model.Gas;
import net.tarine.gastronomi.server.persistence.ForumDao;
import net.tarine.gastronomi.server.persistence.PmFactory;
import net.tarine.gastronomi.shared.DatabaseException;
import net.tarine.gastronomi.shared.EmptyResultException;
import net.tarine.gastronomi.shared.to.ForumMessageTo;
import net.tarine.gastronomi.shared.to.ForumThreadTo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
public class ForumServiceImpl extends RemoteServiceServlet implements ForumService {
	private static final long serialVersionUID = 6005167698499373208L;

	private static final Log LOG = LogFactory.getLog(ForumServiceImpl.class);

	@Override
	public List<ForumThreadTo> findForumThreads(String gasId, Integer offset, Integer pageSize)
			throws DatabaseException, EmptyResultException {
		List<ForumThreadTo> result = new ArrayList<ForumThreadTo>();
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		try {
			Gas gas = pm.getObjectById(Gas.class, gasId);
			List<ForumThread> items = ForumDao.findForumThreads(pm, gas, offset, pageSize);
			for (ForumThread ft:items) {
				result.add(ToConverter.convert(pm, ft));
			}
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		if (result != null) {
			if (result.size() > 0) {
				return result;
			}
		}
		throw new EmptyResultException("Non e' stata trovata alcuna discussione");
	}



	@Override
	public ForumThreadTo findForumThreadById(String id) throws DatabaseException, EmptyResultException {
		ForumThreadTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		try {
			ForumThread item = pm.getObjectById(ForumThread.class, id);
			result = ToConverter.convert(pm, item);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		if (result != null) {
			return result;
		}
		throw new EmptyResultException("Non e' stata trovata alcuna discussione con id="+id);
	}

	
	@Override
	public ForumThreadTo createForumThread(String gasId) throws DatabaseException {
		ForumThreadTo thread = new ForumThreadTo();
		thread.setCreationDt(new Date());
		thread.setGasId(gasId);
		List<ForumMessageTo> msgList = new ArrayList<ForumMessageTo>();
		thread.setForumMessagesToList(msgList);
		return thread;
	}

	
	@Override
	public Boolean deleteForumThread(String id)
			throws DatabaseException, EmptyResultException {
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    ForumThreadTo persistent = pm.getObjectById(ForumThreadTo.class, id);
		    pm.deletePersistent(persistent);
		    tx.commit(); // Commit the PM transaction
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return true;
	}



	@Override
	public ForumThreadTo addForumThreadMessage(ForumThreadTo threadTo,
			ForumMessageTo msgTo) throws DatabaseException {
		Date now = new Date();
		
		//Save/update operation
		ForumThreadTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
			tx.begin(); // Start the PM transaction
			ForumThread thread = null;
			if (threadTo.getId() == null) {
				thread = ToConverter.convert(pm, threadTo);
				pm.makePersistent(thread);
			} else {
				thread = pm.getObjectById(ForumThread.class, threadTo.getId());
			}
			thread.setModifiedDt(now);
			thread.setLastMessageDt(now);
			//note: http://stackoverflow.com/questions/1482570/updating-nested-objects-with-jdo-on-google-app-engine
			ForumMessage msg = ToConverter.convert(pm, msgTo, thread);
			if (msg.getCreationDt() == null) msg.setCreationDt(now);
			msg.setModifiedDt(now);
			msg.setForumThread(thread);
			thread.getForumMessageList().add(msg);
			pm.makePersistent(thread);
			tx.commit(); // Commit the PM transaction
			result = ToConverter.convert(pm, thread);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return result;
	}



	@Override
	public ForumMessageTo findForumMessageById(String id) throws DatabaseException, EmptyResultException {
		ForumMessageTo result = null;
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		try {
		    ForumMessage item = pm.getObjectById(ForumMessage.class, id);
		    ForumThreadTo threadTo = ToConverter.convert(pm, item.getForumThread());
		    result = ToConverter.convert(pm, item, threadTo);
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		if (result != null) {
			return result;
		}
		throw new EmptyResultException("Non e' stato trovato alcun messaggio con id="+id);
	}


	@Override
	public ForumMessageTo createForumMessage() throws DatabaseException {
		ForumMessageTo message = new ForumMessageTo();
		message.setCreationDt(new Date());
		message.setText("");
		return message;
	}


	@Override
	public Boolean deleteForumMessage(String id)
			throws DatabaseException, EmptyResultException {
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    ForumMessage persistent = pm.getObjectById(ForumMessage.class, id);
		    pm.deletePersistent(persistent);
		    tx.commit(); // Commit the PM transaction
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return true;
	}



	@Override
	public Boolean subscribeThread(String threadId, String gasUserId, boolean subscribed)
			throws DatabaseException {
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		Transaction tx = pm.currentTransaction();
		try {
		    tx.begin(); // Start the PM transaction
		    ForumThread thread = pm.getObjectById(ForumThread.class, threadId);
		    if (thread.getSubscribedUserIdList().contains(gasUserId) && !subscribed) {
		    	thread.getSubscribedUserIdList().remove(gasUserId);
		    } else {
		    	if (!thread.getSubscribedUserIdList().contains(gasUserId) && subscribed) {
		    		thread.getSubscribedUserIdList().add(gasUserId);
		    	}
		    }
		    tx.commit(); // Commit the PM transaction
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			tx.rollback(); // Error occurred so rollback the PM transaction
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return subscribed;
	}


	@Override
	public List<String> findThreadSubscriberIds(String threadId)
			throws DatabaseException {
		List<String> result = new ArrayList<String>();
		PersistenceManager pm = PmFactory.get().getPersistenceManager();
		try {
		    ForumThread thread = pm.getObjectById(ForumThread.class, threadId);
		    for (String userId:thread.getSubscribedUserIdList()) {
		    	result.add(userId);
		    }
		} catch (Exception e) {
			LOG.error(e.getMessage(), e);
			throw new DatabaseException(e);
		} finally {
		    pm.close();
		}
		return result;
	}


}
